<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>进阶：13、Render函数 createElement参数</title>
	<script type="text/javascript" src="https://unpkg.com/vue/dist/vue.js"></script>
</head>
<body>
	<div id="app"> 
		<test></test><br/><br/>
		<son1 msg="message">
			<p slot="name-of-slot1">son1's name-of-slot</p>
		</son1>
		<son2>
			<p slot="name-of-slot2">son2's name-of-slot</p>
		</son2>

		<anchored-heading level="3">66666</anchored-heading>
	</div>

	<script type="text/javascript">
		// vue封装的createElement()参数参数分为以下三部分：
		// 第一个参数是要创建的标签类型（必需）
		// 第二个参数是一个包含模板相关属性的数据对象（可选）
		// 第三个参数是子节点(VNodes)，可以是一个字符串或者一个数组.（可选）
		// 
		// 实例演示：
		Vue.component('test',{
			render: function(createElement){
				return createElement(
						// 这里创建了一个div元素
						'div',
						// 元素模板相关属性数据对象
						{
							// 和`v-bind:class`一样的 API
							'class': {
								foo: true,
								bar: false
							},
							// 和`v-bind:style`一样的 API
							style: {
								color: 'red',
								fontSize: '14px'
							},							
							attrs: {
							 	id: 'foo'
							},
							// 组件 props
							// 这里组件的意思是创建的元素不是原生元素，而是一个组件元素
							// 将bar传递给组件元素的myProp属性
							// 如果这个组件元素叫<component-ele>
							// 那么这里的作用相当于
							// <component-ele :myProp="bar">
							// vm2弄个例子试试
							props: {
								myProp: 'bar'
							},
							// DOM 属性
							domProps: {
								// 使用了这个属性会把所有的子节点覆盖掉
								// innerHTML: 'baz' 
							},	
							// 事件监听器基于 "on"
							// 所以不再支持如 v-on:keyup.enter 修饰器
							// 需要手动匹配 keyCode。
							on: {
								click: this.clickHandler
							},
							// 仅对于组件，用于监听原生事件，而不是组件内部使用 vm.$emit 触发的事件。
							// 这个也很好理解，必须创建的元素是组件元素才生效，相当于给组件绑定原生
							// 事件回调函数
							// nativeOn: {
							// 	click: this.nativeClickHandler
							// },
							// 自定义指令. 注意事项：不能对绑定的旧值设值
							// Vue 会为您持续追踪
							// directives: [
							// 	{
							// 	  name: 'my-custom-directive',
							// 	  value: '2',
							// 	  expression: '1 + 1',
							// 	  arg: 'foo',
							// 	  modifiers: {
							// 	    bar: true
							// 	  }
							// 	}
							// ],
							// Scoped slots in the form of
							// { name: props => VNode | Array<VNode> }
							scopedSlots: {
								default: props => createElement('span', props.text)
							},
							// 如果组件是其他组件的子组件，需为slot指定名称							
							slot: 'name-of-slot',
							// 其他特殊顶层属性
							key: 'myKey',
							ref: 'myRef'													
						},
						[
							createElement('h1', 'Hello World!'),
							// 子节点也可以是一个字符串，就是div里面的文本内容啦！
							'bar'
						]
					)
			},
			methods: {
				clickHandler: function(){
					alert('test success!')					
				}
			}
		})

		Vue.component('son1',{
			template: '<div><span>{{ msg }}</span><slot name="name-of-slot1"></slot></div>',
			props: ['msg']
		})
		Vue.component('son2',{
			render: function(createElement){
				return createElement(
						'son1',
						{
							props: {
								msg: "son2's son1"
							},
							nativeOn: {
								click: this.nativeClickHandler
							},			
							// 如果组件是其他组件的子组件，需为slot指定名称
							// 这两个slot暂时没搞明白囧rz，后面的例子会解释
							// slot: 'name-of-slot2',		
							// scopedSlots: ''		
						}
					)
			},
			methods: {
				nativeClickHandler: function(){
					alert("son2's son1")
				}
			}
		})

		// 找到最里面的标签的文本内容
		var getChildrenTextContent = function (children) {
		  return children.map(function (node) {
		    return node.children
		      ? getChildrenTextContent(node.children)
		      : node.text
		  }).join('')
		}
		// ------------------------------ 完整实例 ---------------------------------
		Vue.component('anchored-heading', {
		  render: function (createElement) {
		    // create kebabCase id
		    var headingId = getChildrenTextContent(this.$slots.default)
		      .toLowerCase()
		      .replace(/\W+/g, '-')
		      .replace(/(^\-|\-$)/g, '')
		    return createElement(
		      'h' + this.level,
		      [
		        createElement('a', {
		          attrs: {
		            name: headingId,
		            href: '#' + headingId
		          }
		        }, this.$slots.default)
		      ]
		    )
		  },
		  props: {
		    level: {
		      type: Number,
		      required: true
		    }
		  }
		})		
		var vm = new Vue({
			el: '#app'			
		})

		// ------------------------------- 约束 ----------------------------------
		// 组件树中的所有 VNodes 必须是唯一的。这意味着，下面的 render function 是无效的：
		// render: function (createElement) {
		//   var myParagraphVNode = createElement('p', 'hi')
		//   return createElement('div', [
		//     // 错误-重复的VNodes
		//     myParagraphVNode, myParagraphVNode
		//   ])
		// }
		// 
		// 如果你真的需要重复很多次的元素/组件，你可以使用工厂函数来实现。
		// 例如，下面这个例子 render 函数完美有效地渲染了 20 个重复的段落：
		// render: function (createElement) {
		//   return createElement('div',
		//     Array.apply(null, { length: 20 }).map(function () {
		//       return createElement('p', 'hi')
		//     })
		//   )
		// }
	</script>
</body>
</html>